//
//  ICAsyncSocketManager.m
//  ICome
//
//  Created by zhangrongwu on 16/3/14.
//  Copyright © 2016年 iCom. All rights reserved.
//

#import "ICAsyncSocketManager.h"
#import "ICAsyncSocketConfig.h"
#import "GCDAsyncSocket.h"
#import "ICNotification.h"
#import "ICSyncManager.h"
#import "ICSocketCommunicationManager.h"
#import "ICTCPrequestPackage.h"
#import "ICHeaderFile.h"
#import "iComeApiManager.h"

@interface ICAsyncSocketManager ()<GCDAsyncSocketDelegate>

@property (nonatomic, assign) int reconnection_time;
@property (nonatomic, assign) int beat_time;    //Used for reconnection
@property (nonatomic, strong) GCDAsyncSocket *socket;
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, strong) NSTimer *beatTimer;
@property (nonatomic, copy) RConnetSuccessBlock rconnetSuccessBlock;
@property (nonatomic, copy) RConnetFailureBlock rconnetFailureBlock;


@end

@implementation ICAsyncSocketManager

+ (ICAsyncSocketManager *)sharedInstance {
    static ICAsyncSocketManager *instance = nil;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

- (id)init {
    self = [super init];
    
    if (self) {
        self.status = -1;
        [self.timer invalidate];
        self.timer = nil;
        [self.beatTimer invalidate];
        self.beatTimer = nil;
    }
    
    return self;
}

#pragma mark - socket actions
- (void)initSocket {
    if ([ICTools stringEmpty:[ICTools getToken]]) {
        return;
    }
    if (_status != -1) {
        NSLog(@"Socket Connect: YES");
        [ICSyncManager sharedInstance].syncLoadingStatus = ICSyncLoadingStatusFinish;
        return;
    }
    
    _status = 0;
    
    self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self
                                             delegateQueue:dispatch_get_main_queue()];

    NSError *err;
    if (![self.socket connectToHost:[iComeApiManager instance].tcpUrl
                             onPort:[iComeApiManager instance].tcprt
                        withTimeout:TIMEOUT error:&err]) {
        self.status = -1;
    } else {
//        [self startTLS];
        [self startConnect];
    }
}

- (void)startTLS {
    if ([[iComeApiManager instance].url hasPrefix:@"https"]) {
        NSMutableDictionary *settings = [[NSMutableDictionary alloc] init];
        [settings setObject:[NSNumber numberWithBool:YES]
                     forKey:GCDAsyncSocketManuallyEvaluateTrust];
        [self.socket startTLS:settings];
    }
}

- (void)startConnect {
    NSString *d = @"";
    if ([ICTools isAllowedNotification]) {
        d  = @"";
    }
    // 建立长连接 自动登录
    NSDictionary *param = @{@"c":@"1",
                            @"p":[ICTools getToken],
                            @"d":d};
    [self socketRequestWithRequestBody:param requestTag:0];
    [ICSyncManager sharedInstance].syncLoadingStatus = ICSyncLoadingStatusConnecting;
}

- (void)socketDidSecure:(GCDAsyncSocket *)sock {
    NSLog(@"socketDidSecure");
    _socket = sock;
    //Do your stuff after this
}

- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler {
    completionHandler(YES);
    return;
    
//  自建ssh/tls证书使用以下方法
//  server certificate
//    SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(trust, 0);
//    CFDataRef serverCertificateData = SecCertificateCopyData(serverCertificate);
//
//    const UInt8* const serverData = CFDataGetBytePtr(serverCertificateData);
//    const CFIndex serverDataSize = CFDataGetLength(serverCertificateData);
//    NSData* cert1 = [NSData dataWithBytes:serverData length:(NSUInteger)serverDataSize];
//
//
//    //local certificate
//    NSString *localCertFilePath = [[NSBundle mainBundle] pathForResource:@"LocalCertificate" ofType:@"cer"];
//    NSData *localCertData = [NSData dataWithContentsOfFile:localCertFilePath];
//    CFDataRef myCertData = (__bridge CFDataRef)localCertData;
//
//
//    const UInt8* const localData = CFDataGetBytePtr(myCertData);
//    const CFIndex localDataSize = CFDataGetLength(myCertData);
//    NSData* cert2 = [NSData dataWithBytes:localData length:(NSUInteger)localDataSize];
//
//
//    if (cert1 == nil || cert2 == nil) {
//        NSLog(@"Certificate NULL");
//        completionHandler(NO);
//        return;
//    }
//
//
//    const BOOL equal = [cert1 isEqualToData:cert2];
//
//    if (equal) {
//
//        NSLog(@"Certificate match");
//        completionHandler(YES);
//
//    }else{
//
//        NSLog(@"Certificate not match");
//        completionHandler(NO);
//    }
}

- (void)disconnectSocket {
    _reconnection_time = -1;
    [_socket disconnect];
    
    [_beatTimer invalidate];
    _beatTimer = nil;
}

- (BOOL)isConnected {
    return [self.socket isConnected];
}

- (void)reconnection {
    if ([[ICTools getNetWorkType] isEqualToString:@"none"] || [[ICTools getNetWorkType] isEqualToString:@"无网络"]) {
        [ICSyncManager sharedInstance].syncLoadingStatus = ICSyncLoadingStatusNotNetWork;
        _reconnection_time = 0;
        return;
    }
    /** 重连机制 先断开 后连接*/
    if (self.status != -1) {
        [_socket disconnect];
    }
    [self initSocket];
}

- (void)reconnectionSuccess:(RConnetSuccessBlock)success
                    Failure:(RConnetFailureBlock)failure {
    self.rconnetSuccessBlock = success;
    self.rconnetFailureBlock = failure;
    [self reconnection];
}

- (void)sendBeat {
    if (_beat_time >= beatLimit) {
        [_beatTimer invalidate];
        _beatTimer = nil;
        // 重连后 处理
        [self reconnectionSuccess:^{
            // 重新连接 成功
            // 请求数组中 未超时则继续发送请求
            [self reSendRequest];
        } Failure:^{
        }];
        
    } else {
        _beat_time++;
        //        NSLog(@"sendBeat:%d", _beat_time);
    }
    NSDictionary *dict = @{@"c":@"3"};
    [self socketRequestWithRequestBody:dict requestTag:0];
    //    NSLog(@"NSThread - %@", [NSThread currentThread]);
}

- (void)reSendRequest {
    [[ICSocketCommunicationManager sharedInstance] reSendRequest];
}


// new tcp request
- (void)socketRequestWithRequestBody:(id)body requestTag:(NSInteger)tag {
    NSData *data = [NSJSONSerialization dataWithJSONObject:body options:NSJSONWritingPrettyPrinted error:nil];
    if (data) {
        [self socketWriteData:data];
//        [self.socket writeData:data withTimeout:-1 tag:tag];
    }
}

#pragma mark - GCDAsyncSocketDelegate
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port {

//    NSMutableDictionary *settings = [[NSMutableDictionary alloc] init];
//    [settings setObject:[NSNumber numberWithBool:YES]
//                 forKey:GCDAsyncSocketUseCFStreamForTLS];
//
//
//    [sock startTLS:settings];
    
    //忽略socket断开发送SIGPIPE信号引起app崩溃
    int fd = [self.socket socketFD];
    int value = 1;
    setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));
    
    _status = 1;
    _reconnection_time = 0;
    [_timer invalidate];
    _timer = nil;
    
    if (!_beatTimer) {
        _beatTimer = [NSTimer scheduledTimerWithTimeInterval:heartBeatTimeinterval
                                                      target:self
                                                    selector:@selector(sendBeat)
                                                    userInfo:nil
                                                     repeats:YES];
    }
//    [ICClickRecord event:@{@"ID":@"start",@"",@""}];
    NSLog(@"Cool, I'm connected! That was easy.");
    NSLog(@"socket:%p didConnectToHost:%@ port:%hu", sock, host, port);
    [ICNotification postNotificationName:NotificationUpdateGroupList object:nil userInfo:nil];
    [ICSyncManager sharedInstance].syncLoadingStatus = ICSyncLoadingStatusConnectComplete;
    
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.rconnetSuccessBlock) self.rconnetSuccessBlock();
    });
}

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
    _status = -1;
    
    if (_reconnection_time >= 0 && _reconnection_time <= kMaxReconnection_time) {
        [_timer invalidate];
        _timer = nil;
        //  reconnectionStrategy
        int time = pow(2, _reconnection_time);
        _timer = [NSTimer scheduledTimerWithTimeInterval:time
                                                  target:self
                                                selector:@selector(reconnection)
                                                userInfo:nil
                                                 repeats:NO];
        _reconnection_time++;
        NSLog(@"socket did reconnection,after %ds try again",time);
    } else {
        if (_reconnection_time >= kMaxReconnection_time) {
            ALERT(@"网络异常");
            [ICSyncManager sharedInstance].syncLoadingStatus = ICSyncLoadingStatusNotNetWork;
            if (self.rconnetFailureBlock) self.rconnetFailureBlock();
        }
        _reconnection_time = 0;
        NSLog(@"socketDidDisconnect:%p withError: %@", sock, err);
    }
}

- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
    [self socketBeginReadData];
}

- (void)beginReadDataTag:(long)tag {
    [self.socket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 maxLength:-1 tag:tag];
}

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    if (data.length > 0) {
        [[ICSocketCommunicationManager sharedInstance] receiveServerData:data callbackIdentifier:tag];
    }
    // receive data _beatTimer back to 0
    [self socketBeginReadData];
    _beat_time = 0;
}

- (void)socket:(GCDAsyncSocket *)sock didReadPartialDataOfLength:(NSUInteger)partialLength tag:(long)tag {
}


- (void)socketWriteData:(NSData *)requestData {
    [self.socket writeData:requestData withTimeout:-1 tag:0];
    [self socketBeginReadData];
}

- (void)socketBeginReadData {
    [self.socket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 maxLength:-1 tag:0];
}

@end

